<?php

namespace simpleHandle\Component\UtilLog;

use Throwable;
use simpleHandle\Exception\UtilException;
use Exception;

class Logger
{
	public string $logDir;
	public string $logDateDir;
	public string $log = "";
	public string $fileName;
	public bool   $output;
	public string $lockFile;
	const SUCCESS  = 0;
	const INFO     = 1;
	const WARNING  = 2;
	const ERROR    = 3;
	const CRITICAL = 4;
	const DEBUG    = 5;

	/**
	 * @throws UtilException
	 */
	public function __construct( string $fileName, string $path = "/", bool $output = false, string $fileLock = "" ) {
		try {
			$this->logDir   = $path;
			$this->output   = $output;
			$this->lockFile = $fileLock;
			$this->filePathIsExist();
			$this->fileName = $fileName . '.log';
		} catch ( Throwable $th ) {
			throw new UtilException( $th->getMessage(), UtilException::Logger_ERROR_CODE );
		}
	}

	/**
	 * @throws UtilException
	 */
	public function filePathIsExist() {
		try {
			$this->logDateDir = $this->logDir . DIRECTORY_SEPARATOR . date( "y-m-d", time() );
			if ( ! empty( $this->lockFile ) ) {
				if ( ! is_dir( $this->logDateDir ) ) {
					$file = fopen( $this->lockFile, "r" );
					if ( $file ) {
						if ( flock( $file, LOCK_EX | LOCK_NB ) ) {
							mkdir( $this->logDateDir, 0777, true );
							flock( $file, LOCK_UN );
						}
					}
					fclose( $file );
				}
			} else {
				if ( ! is_dir( $this->logDateDir ) ) {
					mkdir( iconv( "UTF-8", "GBK", $this->logDateDir ), 0777, true );
				}
			}
		} catch ( Throwable $th ) {
			if ( strpos( $th->getMessage(), 'mkdir' ) === false ) {
				throw new UtilException( $th->getMessage(), UtilException::Logger_ERROR_CODE );
			}
		}
	}

	/**
	 * @throws UtilException
	 */
	public function appendLog( $log, $lev = self::INFO ) {
		try {
			$logString = "";
			switch ( $lev ) {
				case self::SUCCESS:
					$logString = "【SUCCESS】 ";
					break;
				case self::INFO:
					$logString = "【INFO】    ";
					break;
				case self::WARNING:
					$logString = "【WARNING】 ";
					break;
				case self::ERROR:
					$logString = "【ERROR】   ";
					break;
				case self::CRITICAL:
					$logString = "【CRITICAL】";
					break;
				case self::DEBUG:
					$logString = "【DEBUG】   ";
					break;
			}
			if ( is_array( $log ) ) {
				$log = json_encode( $log, JSON_UNESCAPED_UNICODE );
			}
			$log       = '【' . date( "Y-m-d H:i:s" ) . '】' . $logString . $log . "\n";
			$this->log .= $log;
		} catch ( Throwable $th ) {
			throw new UtilException( $th->getMessage(), UtilException::Logger_ERROR_CODE );
		}
	}

	/**
	 * @throws UtilException
	 */
	public function writeLog( $log, $lev = self::INFO ) {
		try {
			$this->appendLog( $log, $lev );
			$this->write();
		} catch ( Throwable $th ) {
			throw new UtilException( $th->getMessage(), UtilException::Logger_ERROR_CODE );
		}
	}

	/**
	 * @throws UtilException
	 */
	public function write() {
		try {
			$this->filePathIsExist();
			$startLog    = "--[" . date( 'Y-m-d H:i:s' ) . "]--start-----------------------------------------\n";
			$endLog      = "--end------------------------------------------------------------------\n";
			$log         = $startLog . $this->log . $endLog;
			$logFileName = $this->logDateDir . DIRECTORY_SEPARATOR . $this->fileName;
			file_put_contents( $logFileName, $log, FILE_APPEND | LOCK_EX );
			if ( $this->output ) {
				echo( $log );
			}
			$this->log = "";
		} catch ( Throwable $th ) {
			throw new UtilException( $th->getMessage(), UtilException::Logger_ERROR_CODE );
		}
	}

	/**
	 * @throws UtilException
	 */
	public function writeLine( $log, $lev = self::INFO ) {
		try {
			$this->filePathIsExist();
			$this->appendLog( $log, $lev );
			$logFileName = $this->logDateDir . DIRECTORY_SEPARATOR . $this->fileName;
			$log         = $this->log;
			file_put_contents( $logFileName, $log, FILE_APPEND | LOCK_EX );
			if ( $this->output ) {
				echo( $log );
			}
			$this->log = "";
		} catch ( Throwable $th ) {
			throw new UtilException( $th->getMessage(), UtilException::Logger_ERROR_CODE );
		}
	}

	/**
	 * @throws Exception
	 */
	public function info( $log ) {
		$this->writeLine( $log );
	}

	/**
	 * @throws Exception
	 */
	public function success( $log ) {
		$this->writeLine( $log, self::SUCCESS );
	}

	/**
	 * @throws Exception
	 */
	public function warning( $log ) {
		$this->writeLine( $log, self::WARNING );
	}

	/**
	 * @throws Exception
	 */
	public function error( $log ) {
		$this->writeLine( $log, self::ERROR );
	}

	/**
	 * @throws Exception
	 */
	public function critical( $log ) {
		$this->writeLine( $log, self::CRITICAL );
	}

	/**
	 * @throws Exception
	 */
	public function debug( $log ) {
		$this->writeLine( $log, self::DEBUG );
	}
}